package cn.sc.summer.auth.service.user.lock;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.sc.summer.constant.project.DateConstant;
import cn.sc.summer.constant.rabbit.RabbitConstant;
import cn.sc.summer.constant.service.Support;
import cn.sc.summer.constant.token.TokenConstant;
import cn.sc.summer.rabbitmq.builder.RabbitBuilder;
import cn.sc.summer.rabbitmq.builder.RabbitMessageBuilderDirector;
import cn.sc.summer.redis.util.RedisHelper;
import cn.sc.summer.auth.service.user.lock.base.BaseUserLockCheck;
import cn.sc.summer.token.encrypt.TokenUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 类名：用户锁校验
 *
 * @author a-xin
 * @date 2024/6/12 10:53
 */
@Slf4j
@Service
public class DefaultUserLockCheckImpl extends BaseUserLockCheck {

    private static final long serialVersionUID = -2127915066664236318L;

    @Value("${token.failLock:2}")
    private Integer FAIL_LOCK_TIME;

    @Value("${token.failMax:3}")
    private Integer FAIL_LOCK_MAX;

    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 校验该用户是否被锁
     *
     * @param userId 用户ID
     */
    @Override
    public void checkUserLoginLock(String userId) {
        String lockKey = TokenConstant.LOCK_USER + userId;
        if (RedisHelper.hasKey(lockKey) && Integer.parseInt(String.valueOf(RedisHelper.get(lockKey))) > FAIL_LOCK_MAX) {
            TokenUtil.deleteUserToken(userId);
            Long expire = RedisHelper.getExpire(lockKey);
            log.error("The account has been locked, please try again in " + expire / 60 + " minutes！");
            throw new AuthenticationServiceException("The account has been locked, please try again in " + expire / 60 + " minutes！");
        }
        Map<Object, Object> userInfoMap = RedisHelper.hmGet(TokenConstant.USER_INFO + userId);
        if (CollUtil.isEmpty(userInfoMap)) {
            log.error("The user :" + userId + " does not exist！");
            throw new AuthenticationServiceException("The user does not exist！");
        }
        if (RedisHelper.hasKey(lockKey) && Integer.parseInt(String.valueOf(RedisHelper.get(lockKey))) == FAIL_LOCK_MAX) {
            if (CollUtil.isNotEmpty(userInfoMap)) {
                RedisHelper.incr(lockKey, 1);
                TokenUtil.deleteUserToken(userId);
            }

            JSONObject message = new RabbitMessageBuilderDirector(new RabbitBuilder()).build();
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("phone", userId);
            dataMap.put("failMax", FAIL_LOCK_MAX);
            dataMap.put("failLock", FAIL_LOCK_TIME);
            dataMap.put("lastLogin", DateUtil.format(new Date(), DateConstant.YYYY_MM_DD_HH_MM_SS_1));

            JSONObject rabbitMessage = new RabbitMessageBuilderDirector(new RabbitBuilder()).build();
            message.put(RabbitConstant.MESSAGE_KEY, JSONObject.toJSONString(dataMap));
            rabbitTemplate.convertAndSend(RabbitConstant.LOGIN_RECORD_EXCHANGE,
                    RabbitConstant.LOGIN_RECORD_ROUTING_KEY,
                    rabbitMessage.toJSONString());

            log.error("The number of failures exceeded and the account has been locked, please try again in " + FAIL_LOCK_TIME + " hours！");
            throw new AuthenticationServiceException("The number of failures exceeded and the account has been locked, please try again in " + FAIL_LOCK_TIME + " hours！");
        }
    }

    /**
     * 添加用户锁
     *
     * @param userId 用户ID
     */
    @Override
    public void addUserLoginLock(String userId) {
        String lockKey = TokenConstant.LOCK_USER + userId;
        RedisHelper.incr(lockKey, 1);
        RedisHelper.expire(lockKey, FAIL_LOCK_TIME, TimeUnit.HOURS);
        log.error("==> User : {}, Login failed number : {} times", userId, RedisHelper.get(lockKey));
    }

    /**
     * 删除用户锁
     *
     * @param userId 用户ID
     */
    @Override
    public void deletedUserLoginLock(String userId) {
        String lockKey = TokenConstant.LOCK_USER + userId;
        RedisHelper.del(lockKey);
    }

    /**
     * Determine the types of data sources supported by this class
     *
     * @return Supported types
     */
    @Override
    public String supports() {
        return Support.DEFAULT;
    }

}
